from main.model.devices.memory.memory_block import MemoryBlock

class BlockNode(object):
    def __init__(self, start, end):
        self.displacement = 0
        self.start = start
        self.end = end
        self.pcb = None

class MemorySmartBlock(object):
    def __init__(self, memory, start, end):
        self.__node = BlockNode(start, end)
        self.__memory = memory
        self.__next = None
        
    def simple_block(self):
        return MemoryBlock(self.start, self.end)
    
    def is_empty(self):
        return not self.pcb
    
    def cut_if_great_than(self, size):
        if(self.size > size):            
            cut = self.start + size 
            new_block = MemorySmartBlock(self.memory, cut, self.end)
            self.end = cut - 1
            new_block.next = self.next
            self.next = new_block
            return self
    
    def compact(self, size):
        if(not self.is_empty()):
            #if actual block is the last and has not size requested
            #then there is a problem, memory has not sufficient free space
            self.next().compact(size)
        else:
            if(self.size > size):
                #it has sufficient free space
                return self
            else:
                #is empty and minor that size
                #swap with next
                self.swap_next()
                self.next.join_next_while_empty()
                return self.next.compact(size)
    
    #assumes self empty
    def swap_next(self):
        self.copy_memory_data(self.next.start, self.start, self.next.size)
        temp_node = self.node
        self.node = self.next.node
        self.next.node = temp_node
        self.move(self.next.size)
        self.next.move( - self.size)
    
    def copy_memory_data(self, origin, destiny, size):
        for index in range(0, size+1):
            self.memory[destiny + index] = self.memory[origin + index]
    
    def join_next_while_empty(self):
        while(self.next and self.next.is_empty()):
            self.end = self.next.end
            self.next = self.next.next
            
    def move(self, value):
        self.displacement += value
        self.start += value
        self.end += value
    
    @property
    def size(self):
        return self.end - self.start + 1
    
    @property
    def displacement(self):
        return self.__node.displacement
    
    @displacement.setter
    def displacement(self, value):
        self.__node.displacement = value
    
    @property
    def next(self):
        return self.__next
    
    @next.setter
    def next(self, value):
        self.__next = value

    @property
    def start(self):
        return self.__node.start

    @start.setter
    def start(self, value):
        self.__node.start = value
        
    @property
    def end(self):
        return self.__node.end
    
    @end.setter
    def end(self, value):
        self.__node.end = value
    
    @property
    def pcb(self):
        return self.__node.pcb
    
    @pcb.setter
    def pcb(self, value):
        self.__node.displacement = 0
        self.__node.pcb = value

    @property
    def memory(self):
        return self.__memory


